<?php

/**
 * Cogsworth
 *
 * An open source multi-application MVC-based framework written in PHP for XML and XSL.
 *
 * Copyright (c) 2008, Daniel Wilhelm II Murdoch
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @package Cogsworth
 * @copyright 2008, Daniel Wilhelm II Murdoch
 * @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
 * @link http://www.thedrunkenepic.com/
 * @license http://www.opensource.org/licenses/gpl-2.0.php GPL License
 */


/***
 * This is an exception handler made specifically for the shopping cart class.
 * It allows a developer to extend on the existing exception handler and create
 * specified methods unique to ShoppingCartClass.It might be a good idea to
 * move this class somewhere else.
 ***/

class ShoppingCartException extends Exception {}


/***
 * Since this class requires access to php sessions, let's first check if
 * we actually have a session activated. If not, let's start one:
 ***/

if(false == isset($_SESSION))
{
	session_start();
}


/**
 * ShoppingCartClass
 *
 * Class ShoppingCartClass is a static class that creates and manages simple shopping carts. All
 * carts are saved to the current user's PHP session. When adding an item to the cart, you only
 * need to worry about 2 pieces of meta data: a unique identifier, and a unit price. The quantity
 * for each item is managed by the class and specific product meta-data can be passed to the
 * class as an associative array. The array passed can be either single- or multi-dimensional.
 *
 * IMPORTANT NOTE: If using standard PHP sessions to manage your carts, it might be a good idea
 * NOT to pass through sensitive information to an item's meta-data when using the 'add()' method.
 * Just in case the session is hijacked.
 *
 * @package Cogsworth
 * @author Daniel Wilhelm II Murdoch
 * @email wilhelm.murdoch@gmail.com
 * @filename cart.core.php
 * @title ShoppingCartClass
 * @url http://www.thedrunkenepic.com/
 * @version 1.0
 */
class ShoppingCartClass
{
	/**
	 * Contains an associative array of items and their contents.
	 * @access Static Private
	 * @type Array
	 */
	static private $shopping_cart = array();


   // ! Constructor Method

   /**
	* We place this here to prevent developers from creating an instance
	* of this class. We want to keep this class static. If someone attempts
	* to instantiate an instance of this class, they'll be welcomed by a
	* nasty PHP error.
	*
	* @param none
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Private
	* @return Void
	*/
	private function __construct() { /* EMPTY */ }


   // ! Executor Method

   /**
	* Adds a item to the current shopping cart.
	*
	* @param Integer $id The unique identifier for the item to add
	* @param Array $meta Any item meta-data stored in an associative array
	* @param Float $price The price of the item
	* @param Integer $quantity The amount of this item to add
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function add($id, $meta = array(), $price = 0, $quantity = 1)
	{
		if(self::exists($id))
		{
			self::$shopping_cart[$id]['quantity'] += (int) $quantity;

			return true;
		}

		self::$shopping_cart[$id] = array('meta' => $meta, 'price' => $price, 'quantity' => (int) $quantity);

		return true;
	}


   // ! Executor Method

   /**
	* Removes a item from the current shopping cart.
	*
	* @param Integer $id The unique identifier for the item to remove
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function remove($id)
	{
		if(false == self::exists($id))
		{
			throw new ShoppingCartException("You cannot remove item '{$id}' from the shopping cart if it does not exist.");

			return false;
		}

		unset(self::$shopping_cart[$id]);

		return true;
	}


   // ! Accessor Method

   /**
	* Returns info for the request item.
	*
	* @param Integer $id The unique identifier for the item to return
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Mixed
	*/
	static public function get($id)
	{
		if(false == self::exists($id))
		{
			throw new ShoppingCartException("You cannot retrieve item '{$id}' from the shopping cart if it does not exist.");

			return false;
		}

		return self::$shopping_cart[$id];
	}


   // ! Executor Method

   /**
	* Updates any aspect of an existing item.
	*
	* @param Integer $id The unique identifier for the item to update
	* @param Array $meta Any item meta-data stored in an associative array
	* @param Float $price The price of the item
	* @param Integer $quantity The amount of this item to set
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function update($id, $meta = array(), $price = 0, $quantity = 0)
	{
		if(false == self::exists($id))
		{
			throw new ShoppingCartException("You cannot update item '{$id}' quantity if it does not exist.");

			return false;
		}

		self::$shopping_cart[$id] = array('meta' => $meta, 'price' => $price, 'quantity' => (int) $quantity);

		return true;
	}


   // ! Mutator Method

   /**
	* Empties the current cart.
	*
	* @param none
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return True
	*/
	static public function flushCart()
	{
		self::$shopping_cart = array();

		return true;
	}


   // ! Executor Method

   /**
	* Empties a saved cart from the current PHP session.
	*
	* @param String $cart_id The unique identifier for the shopping cart to remove
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function flushSession($cart_id = 'cart')
	{
		if(false == isset($_SESSION[$cart_id]))
		{
			throw new ShoppingCartException("You cannot remove cart '{$cart_id}' from session as it does not exist.");

			return false;
		}

		unset($_SESSION[$cart_id]);

		return true;
	}


   // ! Executor Method

   /**
	* Saves a cart to the current PHP session and will, optionally, empty the
	* current cart stored within this static class.
	*
	* @param String $cart_id The unique identifier for the shopping cart to save
	* @param Bool $flush_on_save If set to True this method will flush the current cart
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return True
	*/
	static public function save($cart_id = 'cart', $flush_on_save = true)
	{
		$_SESSION[$cart_id] = self::$shopping_cart;

		if($flush_on_save)
		{
			self::flushCart();
		}

		return true;
	}


   // ! Accessor Method

   /**
	* Returns the contents of the current cart.
	*
	* @param none
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Array
	*/
	static public function all()
	{
		return self::$shopping_cart;
	}


   // ! Accessor Method

   /**
	* Returns the gross cost of all items within the shopping cart.
	*
	* @param none
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Float
	*/
	static public function getTotalPrice()
	{
		$total = 0;

		foreach(self::$shopping_cart as $item)
		{
			$total += (int) $item['price'] * (int) $item['quantity'];
		}

		return number_format($total, 2);
	}


   // ! Accessor Method

   /**
	* Returns the gross cost a single item from within the shopping cart.
	* It will optionally return the unit price of an item as well.
	*
	* @param Integer $id Identifier of the item
	* @param Bool $unit Set to true if you only want the unit price of the item
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Float
	*/
	static public function getSinglePrice($id, $unit = false)
	{
		if(false == self::exists($id))
		{
			throw new ShoppingCartException("You cannot calculate the price of an item '{$id}' if it does not exist.");

			return false;
		}

		if(false == $unit)
		{
			return number_format((int) (self::$shopping_cart[$i]['price'] * self::$shopping_cart[$i]['quantity']), 2);
		}

		return number_format((int) self::$shopping_cart[$i]['price'], 2);
	}


   // ! Accessor Method

   /**
	* Returns the total number of items in a shopping cart. If the $unique parameter
	* is set to True, this method will return only the number of different items
	* based on item id.
	*
	* @param none
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function count($unique = false)
	{
		if($unique)
		{
			return sizeof(self::$shopping_cart);
		}

		$count = 0;

		foreach(self::$shopping_cart as $item)
		{
			$count += (int) $item['quantity'];
		}

		return $count;
	}


   // ! Accessor Method

   /**
	* Checks if a particular item has been added.
	*
	* @param Integer $id Identifer of the item to look for
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function exists($id)
	{
		return isset(self::$shopping_cart[$id]);
	}


   // ! Accessor Method

   /**
	* A simple method to check if the shopping cart is empty.
	*
	* @param none
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Static Public
	* @return Bool
	*/
	static public function isEmpty()
	{
		return false == self::$shopping_cart ? true : false;
	}
}

?>